package com.rzjm.exercise.common.utils;

import com.rzjm.exercise.common.exception.ConvertFailedException;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.Temporal;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Pattern;

public class DateTimeUtils {
	// regular express for 'yyyy-MM-dd HH:mm:ss' style datetime string 
	private static final String REGEXP_DATETIME_PATTERN = "^20\\d{2}-(0?[1-9]|1[0-9])-(0?[1-9]|[12]\\d|3[01])\\s(0?\\d|1\\d|2[0-3]):(0?|[1-5])\\d:(0?|[1-5])\\d$";
	// regular express for 'yyyy-MM-dd' style date string 
	private static final String REGEXP_DATE_PATTERN = "^20\\d{2}-(0?[1-9]|1[0-9])-(0?[1-9]|[12]\\d|3[01])$";
	// regular express for 'HH:mm:ss' style time string 
	private static final String REGEXP_TIME_PATTERN = "^(0?\\d|1\\d|2[0-3]):(0?|[1-5])\\d:(0?|[1-5])\\d$";
	// regular express for 'yyyy?MM?dd' style date string, ? can be -,/,NULL 
	private static final String REGEXP_EXT_DATE_PATTERN = "^20\\d{2}(-|\\/)(0?[1-9]|1[0-9])(-|\\/)(0?[1-9]|[12]\\d|3[01])$";
	
	// formatter to format java.util.Date to string
	private static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
	private static final SimpleDateFormat CNDATE_FORMATTER = new SimpleDateFormat("MM月dd日");
	private static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("HH:mm:ss");
	private static final SimpleDateFormat HOUR_FORMATTER = new SimpleDateFormat("HH:mm");

	// formatter to format string to java.time.LocalDateTime or java.time.LocalDate or java.time.LocalTime, vice versa
	private static final DateTimeFormatter LOCAL_DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
	private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
	private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
	
	// formatter to format java.time.LocalDateTime or java.time.LocalDate or java.time.LocalTime to serial number
	private static final DateTimeFormatter DATE_SERIAL_NUMBER_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

	public static final String getDateSerialNumber(LocalDateTime dateTime) {
		return dateTime.format(DATE_SERIAL_NUMBER_FORMATTER);
	}


	/**
	 * 获取yyyy-MM-dd格式
	 * @return
	 */
	public static String getTimesStr(Date date) {
		return DATE_FORMATTER.format(date);
	}


	public static final String getDateSerialNumber(LocalDate date) {
		return date.format(DATE_SERIAL_NUMBER_FORMATTER);
	}
	
	public static final String formatLocalDateTime(final LocalDateTime dateTime, final String pattern) {
		if (dateTime == null) {
			return null;
		}
		
		if (StringUtils.isEmpty(pattern)) {
			return formatLocalDateTime(dateTime);
		}
		
		return dateTime.format(DateTimeFormatter.ofPattern(pattern));
	}
	
	public static final String formatLocalDateTime(final LocalDateTime dateTime) {
		if (dateTime == null) {
			return null;
		}
		
		return dateTime.format(LOCAL_DATETIME_FORMATTER);
	}
	
	public static final String formatDateTime(final Date dateTime) {
		if (dateTime == null) {
			return null;
		}
		
		return DATETIME_FORMATTER.format(dateTime);
	}
	
	public static final String formatTime(final Date Time) {
		if (Time == null) {
			return null;
		}
		
		return TIME_FORMATTER.format(Time);
	}

	public static final String formatHour(final Date Time) {
		if (Time == null) {
			return null;
		}

		return HOUR_FORMATTER.format(Time);
	}
	
	public static final String formatLocalDate(final LocalDate date) {
		if (date == null) {
			return null;
		}
		
		return date.format(LOCAL_DATE_FORMATTER);
	}

	public static String getWeekOfDate(Date date) {
		String[] weekDays = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"};
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
		if (w < 0) {
			w = 0;
		}

		return weekDays[w];
	}

	public static final String formatDate(final Date date) {
		if (date == null)
			return null;
		
		return DATE_FORMATTER.format(date);
	}

	public static final String formatCNDate(final Date date) {
		if (date == null)
			return null;

		return CNDATE_FORMATTER.format(date);
	}
	
	public static final String formatLocalTime(final LocalTime time) {
		if (time == null) 
			return null;
		
		return LOCAL_TIME_FORMATTER.format(time);
	}

	/**
	 * 格式化日期
	 * @return
	 */
	public static String fomatDateTime(String dateStr,String format) {
		DateFormat fmt = new SimpleDateFormat(format);
		try {
			Date date = fmt.parse(dateStr);
			return DATETIME_FORMATTER.format(date);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 格式化日期
	 * @return
	 */
	public static Date fomatDateTime(String date) {
		DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		try {
			return fmt.parse(date);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static final Date parseDate(final String source) {
		if (StringUtils.isEmpty(source)) {
			return null;
		}
		
		if (Pattern.matches(REGEXP_DATETIME_PATTERN, source)) {
			try {
				return DATETIME_FORMATTER.parse(source);
			} catch (ParseException pe) {
				/* ignore */
			}
		}
		
		if (Pattern.matches(REGEXP_DATE_PATTERN, source)) {
			try {
				return DATE_FORMATTER.parse(getCorrectedDateString(source));
			} catch (ParseException pe) {
				/* ignore */
			}
		}
		
		try {
			return DateFormat.getDateTimeInstance().parse(source);
		} catch (ParseException pe) {
			/* ignore */
		}
		
		try {
			return DateFormat.getTimeInstance().parse(source);
		} catch (ParseException e) {
			/* ignore */
		}

        throw new ConvertFailedException(String.class, Date.class, source);
	}
	
	public static final LocalDate parseLocalDate(final String source) {
		if (StringUtils.isEmpty(source)) {
			return null;
		}
		
		if (Pattern.matches(REGEXP_DATE_PATTERN, source)) {
			return LocalDate.parse(getCorrectedDateString(source), LOCAL_DATE_FORMATTER);
		}
		
		if (Pattern.matches(REGEXP_DATETIME_PATTERN, source)) {
			return LocalDateTime.parse(getCorrectedDateTimeString(source), LOCAL_DATETIME_FORMATTER).toLocalDate();
		}
		
		if (Pattern.matches(REGEXP_EXT_DATE_PATTERN, source)) {
		    return parseLocalDate(StringUtils.replace(source, "/", "-"));
		}

		try {
			return LocalDate.parse(source);
		} catch (DateTimeParseException dtpe) {
			/* ignore */
		}
		
		throw new ConvertFailedException(String.class, LocalDate.class, source);
	}
	
	public static final LocalDateTime parseLocalDateTime(final String source) {
		if (StringUtils.isEmpty(source)) {
			return null;
		}
		
		if (Pattern.matches(REGEXP_DATE_PATTERN, source)) {
			return LocalDate.parse(getCorrectedDateString(source), LOCAL_DATE_FORMATTER).atStartOfDay();
		}
		
		if (Pattern.matches(REGEXP_DATETIME_PATTERN, source)) {
			return LocalDateTime.parse(getCorrectedDateTimeString(source), LOCAL_DATETIME_FORMATTER);
		}
		
		try {
			return LocalDateTime.parse(source);
		} catch (DateTimeParseException dtpe) {
			/* ignore */
		}
		
		throw new ConvertFailedException(String.class, LocalDateTime.class, source);
	}
	
	public static final LocalTime parseLocalTime(final String source) {
		if (StringUtils.isEmpty(source)) {
			return null;
		}
		
		if (Pattern.matches(REGEXP_TIME_PATTERN, source)) {
			return LocalTime.parse(getCorrectedTimeString(source), LOCAL_TIME_FORMATTER);
		}
		
		if (Pattern.matches(REGEXP_DATETIME_PATTERN, source)) {
			return LocalDateTime.parse(getCorrectedDateTimeString(source), LOCAL_DATETIME_FORMATTER).toLocalTime();
		}
		
		try {
			return LocalTime.parse(source);
		} catch (DateTimeParseException dtpe) {
			/* ignore */
		}
		
		throw new ConvertFailedException(String.class, LocalTime.class, source);
	}
	
	public static final long getDurationInMilliseconds(Temporal start, Temporal end) {
		return ConvertUtils.convert(end, Long.class) - ConvertUtils.convert(start, Long.class);
	}
	
	@SuppressWarnings("unchecked")
	public static final <T> T addDay(T time, int day) {
		return (T) ConvertUtils.convert(new Date(ConvertUtils.convert(time, Date.class).getTime() + 1000 * 60 * 60 * 24 * day), time.getClass());
	}
	
	//-------------------------------------------------------------------------------
	// 私有方法
	//-------------------------------------------------------------------------------
	/**
	 * 修正非标准化的日期，标准化的日期格式为{@link DateTimeUtils#DATE_FORMATTER}<br>
	 * 因浏览器、客户导入等问题导致部分日期格式有错误，如：2018-01-1，2018-1-2等等
	 */
	private static final String getCorrectedDateString(String date) {
	    if (Pattern.matches("\\d[^\\d]+.*|.*-\\d($|[^\\d]+.*)", date)) {
	        String[] strs = StringUtils.split(date, "-");
	        
	        // 月份
	        if (strs[1].length() == 1) {
	            strs[1] = "0" + strs[1];
	        }
	        
	        // 日期
	        if (strs[2].length() == 1) {
                strs[2] = "0" + strs[2];
            }
	        
	        return ArrayUtils.arrayToDelimitedString(strs, "-");
	    }
	    
	    return date;
	}
	
	/**
     * 修正非标准化的日期时间，标准化的日期时间格式为{@link DateTimeUtils#DATETIME_FORMATTER}<br>
     * 因浏览器、客户导入等问题导致部分日期时间格式有错误，如：2018-01-1 12:01:1，2018-1-2 15:1:02等等
     */
    private static final String getCorrectedDateTimeString(String dateTime) {
        String[] strs = StringUtils.split(dateTime, " ");
        
        return getCorrectedDateString(strs[0]) + " " + getCorrectedTimeString(strs[1]);
    }
    
    /**
     * 修正非标准化的时间，标准化的日期格式为{@link DateTimeUtils#TIME_FORMATTER}<br>
     * 因浏览器、客户导入等问题导致部分时间格式有错误，如：12:01:1，15:1:02等等
     */
    private static final String getCorrectedTimeString(String time) {
        if (Pattern.matches("\\d[^\\d]+.*|.*:\\d($|[^\\d]+.*)", time)) {
            String[] strs = StringUtils.split(time, ":");
            
            // 小时
            if (strs[0].length() == 1) {
                strs[0] = "0" + strs[0];
            }
            
            // 分钟
            if (strs[1].length() == 1) {
                strs[1] = "0" + strs[1];
            }
            
            // 秒
            if (strs[2].length() == 1) {
                strs[2] = "0" + strs[2];
            }
            
            return ArrayUtils.arrayToDelimitedString(strs, "-");
        }
        
        return time;
    }

	/**
	 * <li>功能描述：时间相减得到天数
	 * @param beginDateStr
	 * @param endDateStr
	 * @return
	 * long
	 * @author Administrator
	 */
	public static long getDaySub(String beginDateStr,String endDateStr){
		long day=0;
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
		Date beginDate = null;
		Date endDate = null;

		try {
			beginDate = format.parse(beginDateStr);
			endDate= format.parse(endDateStr);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		day=(endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000);
		//System.out.println("相隔的天数="+day);
		return day;
	}
	/**
	 * <li>功能描述：时间相减得到天数
	 * @param beginDate
	 * @param endDate
	 * @return
	 * long
	 * @author Administrator
	 */
	public static long getDayCountSub(Date beginDate, Date endDate){
		long day =(endDate.getTime() - beginDate.getTime()) / (24 * 60 * 60 * 1000);
		return day;
	}
}